//Learned:
//1.创建成功和失败都打一下日志！
//2.sockaddr_in类型的结构体可以直接创建，而不用作为成员变量！
//3.收多少就发多少，每次echo时不用发整个缓冲区！
//4.client端ip为0.0.0.0可以正常运行，但为云服务器ip时服务器端接收不到消息？ --> 云服务器上安全组中没有开放udp协议的端口！
//5.广播可以单独写也可以和Run耦合在一起；在线用户表的维护可以也一起封装到UdpServer类中
#include "log.hpp"
#include <string>
#include <cstring>

#include <sys/types.h>         
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <unordered_map>
using std::string;

const int DefalutPort = 8080;
const string DefalutIp = "0.0.0.0";
#define NUM 4096
typedef std::function<string(const string&)> func_t;

//<socket,ip>
//std::unordered_map<sockaddr_in,string> UserList;
std::vector<sockaddr_in> UserList;
enum
{
    SOCKERR = 1,
    BINDERR
};

class UdpServer
{
    
public:
    void Init()
    {
        //1.获取socket
        if((_socketfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
        {
            _log(Fatal,"socket error! error message: %s", strerror(errno));
            exit(SOCKERR);
        }
        _log(Info,"socketfd create successfully, value is: %d", _socketfd);

        //2.结构体字段初始化        //成员变量和直接创建变量会影响结果吗？--> 测试来看不会
        sockaddr_in _server;
        bzero(&_server, sizeof(_server));
        _server.sin_family = AF_INET;
        _server.sin_port = htons(_port);    //l和s会影响结果吗？--> 测试来看不会
        _server.sin_addr.s_addr = inet_addr(_ip.c_str());

        // sockaddr_in local;
        // bzero(&local, sizeof(local));
        // local.sin_family = AF_INET;
        // local.sin_port = htons(_port);   
        // local.sin_addr.s_addr = inet_addr(_ip.c_str());

        //3.绑定socket
        if(bind(_socketfd, (sockaddr*)&_server, sizeof(_server)) < 0)
        {
            _log(Fatal,"bind error! error message: %s", strerror(errno));
            exit(BINDERR);
        }
        _log(Info,"bind success! message: %s", strerror(errno));

        // if(bind(_socketfd, (sockaddr*)&local, sizeof(local)) < 0)
        // {
        //     _log(Fatal,"bind error! error message: %s", strerror(errno));
        //     exit(BINDERR);
        // }
        // _log(Info,"bind success! message: %s", strerror(errno));
    }

    void Run(func_t func)
    {
        while(true)
        {
            sockaddr_in temp;
            socklen_t len = sizeof(temp);
            char buffer[NUM] = {0}; 
            ssize_t n = recvfrom(_socketfd, buffer,sizeof(buffer),0,(sockaddr*)&temp, &len);    //接收信息来初始化sockaddr_in结构体对象

            if(n < 0)
            {
                _log(Error,"recvfrom error! error message: %s", strerror(errno));
            }

            string msg = func(buffer);
            //string msg = "ip: " ;
            //msg += inet_ntoa(temp.sin_addr);
            if(sendto(_socketfd, msg.c_str(), msg.size()+1, 0, (sockaddr*)&temp, (socklen_t)sizeof(temp))< 0)
            {
                _log(Error,"sendto error! error message: %s", strerror(errno));
            }
        }
    }

    bool existCheck(const sockaddr_in& sock)
    {
        for(auto& e : UserList)
        {
            if(e.sin_addr.s_addr == sock.sin_addr.s_addr)
            {
                return true;
            }
        }
        return false;
    }


    void BroadCast()
    {
        char buffer[NUM] = {0};
        while(true)
        {
            sockaddr_in client;
            socklen_t len = sizeof(client);
            ssize_t n = recvfrom(_socketfd, buffer,sizeof(buffer),0,(sockaddr*)&client, &len);    //接收信息来初始化sockaddr_in结构体对象
            if(n < 0)
            {
                _log(Error,"recvfrom error! error message: %s", strerror(errno));
            }

            // if(UserList.count(temp) == 0)
            // {
            //     UserList[temp] = string(inet_ntoa(temp.sin_addr));  //加入广播列表
            // }
            if(!existCheck(client))        
            {
                UserList.push_back(client);
                cout << "now user number is: " << UserList.size() << endl;
            }
            
            //简单拼接消息
            string msg = "[ip: " + string(inet_ntoa(client.sin_addr)) + "]says: " + string(buffer);

            for(auto& e : UserList)
            {
                if(sendto(_socketfd, msg.c_str(), msg.size()+1, 0, (sockaddr*)&e, (socklen_t)sizeof(e))< 0)
                {
                    _log(Error,"sendto error! error message: %s", strerror(errno));
                }                
            }
            memset(buffer,0,sizeof(buffer));
        }
    }

    UdpServer(int port = DefalutPort, const string& ip = DefalutIp)
    :_socketfd(0),_port(port),_ip(ip)
    {}

    ~UdpServer()
    {
        if(close(_socketfd) < 0)
        {
            _log(Error,"close error! error message: %s", strerror(errno));
        }
    }
private:
    int _socketfd;
    int _port;
    string _ip;
    //sockaddr_in _server;
    Log _log;
    //std::unordered_map<sockaddr_in,string> _UserList;     //???
};

